/*
 * Copyright (C) 2012 Tapas Mobile Ltd.  All Rights Reserved.
 */

package com.tools.cleanmaster.appinfo;

import android.annotation.TargetApi;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.graphics.drawable.Drawable;

import com.tools.cleanmaster.CleanApplication;
import com.tools.cleanmaster.utils.StorageUtils;

import java.io.File;
import java.lang.ref.WeakReference;
/**
 * The app info bean
 *
 * @author billy
 *
 * @see AppManagerCompat
 */
public class AppInfoCompat {
    public static final int ENABLE_ENABLE = 1;
    public static final int ENABLE_DISABLE = -1;
    public static final int ENABLE_UNKNOWN = 0;

    public String pkgName;
    public int uid = -1;
    public boolean isSystem;
    private String mLabel;
    private WeakReference<PackageInfo> mPackage;
    private WeakReference<Drawable> mIcon;
    private File mApkFile;
    private int mFlag = 0;
    private int mEnabled = ENABLE_UNKNOWN;
    private long mSize = -1;
    private long mAppSize = -1;
    private long mInstallTime = -1;
    private long mUpdateTime = -1;
    private boolean mIsValid = true;
    private long mBaiduMd5 = 0L;

    AppInfoCompat(String pkg) {
        pkgName = pkg;
        mIsValid = false;
    }

    AppInfoCompat(PackageInfo info) {
        pkgName = info.packageName;
        updateInfo(info);
    }

    private synchronized void init() {
        if (mIsValid && mPackage == null) {
            getPackage();
        }
    }

    private void updateInfo(PackageInfo info) {
        synchronized (this) {
            mPackage = new WeakReference<PackageInfo>(info);
            if (null == info.applicationInfo) {
                return;
            }
            mFlag = info.applicationInfo.flags;
            int enable = AppManagerCompat.getApplicationEnabledSetting(pkgName);
            /**
             * we don't call info.applicationInfo.enabled directly here because
             * in some system, this value always return true.
             */
            mEnabled = (enable == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT ||
                    enable == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) ? ENABLE_ENABLE
                            : ENABLE_DISABLE;
            uid = info.applicationInfo.uid;
            isSystem = (mFlag & ApplicationInfo.FLAG_SYSTEM) == ApplicationInfo.FLAG_SYSTEM ||
                    (mFlag & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
            String source = info.applicationInfo.sourceDir;
            mApkFile = (source != null) ? new File(source) : null;
            if (mInstallTime <= 0) {
                mInstallTime = -1;
                getInstallTime();
            }
        }
    }

    public boolean isValid() {
        return mIsValid;
    }

    public boolean isSystem(){
        return isSystem;
    }

    public int getFlag() {
        init();
        return mFlag;
    }

    public String getSourceDir() {
        init();
        if (mApkFile != null) return mApkFile.getAbsolutePath();
        return null;
    }

    public boolean enable() {
        init();
        return mEnabled == ENABLE_ENABLE;
    }

    /**
     * get the PackageInfo
     * @return null if not valid or can not found the package
     */
    public PackageInfo getPackage() {
        PackageInfo app = null;
        if (mIsValid && (mPackage == null || (app = mPackage.get()) == null)) {
            app = AppManagerCompat.getInstance().fetchPackage(pkgName);
            if (app == null) return null;
            updateInfo(app);
        }
        return app;
    }


    /**
     * Get signature digest with BaiduMD5</p>
     * Note:</br>
     * Cannot call this method in UI thread.</p>
     * @return 0 will be returned if the app not found or other exceptions happen
     */
    public long getSignatureBaiduMd5() {
        if (mBaiduMd5 != 0) {
            return mBaiduMd5;
        }

        if (mBaiduMd5 == 0) {
            Signature[] signs = getSignatures();
            if (signs != null && signs.length > 0) {
                mBaiduMd5 = AppSignatureUtils.computeSignatureBaiduMd5(signs);
            }
        }
        return mBaiduMd5;
    }

    public Signature[] getSignatures() {
        PackageInfo pi = getPackage();
        if (pi == null) return null;
        if (pi.signatures == null) {
            pi = AppManagerCompat.getInstance().fetchPackage(pkgName);
            if (pi == null) return null;
            mPackage = new WeakReference<PackageInfo>(pi);
        }
        return pi.signatures;
    }

    private Drawable loadIcon() {
        Drawable icon = AppManagerCompat.getIcon(this);
        if (icon != null) {
            mIcon = new WeakReference<Drawable>(icon);
        }
        return icon;
    }

    /**
     * get the icon without delay
     *
     * @return the icon, should be null if invalid or package not found
     */
    public Drawable getIcon() {
        if (!mIsValid) {
            return AppManagerCompat.getIcon(this);
        }
        init();
        Drawable icon = null;
        if (mIcon == null || (icon = mIcon.get()) == null) {
            if (mApkFile == null || !mApkFile.exists()) {
                return null;
            }
            icon = loadIcon();
        }
        return icon;
    }

    public String getLabel() {
        if (!mIsValid) {
            return AppManagerCompat.getLabel(this);
        }
        init();
        if (mLabel == null) {
            if (mApkFile == null || !mApkFile.exists()) return pkgName;
            PackageInfo info = getPackage();
            if (info == null) return pkgName;

            mLabel = info.applicationInfo.loadLabel(
                    CleanApplication.getInstance().getPackageManager()).toString();
            AppManagerCompat.setLabel(pkgName, mLabel);
        }
        return mLabel;
    }

    private long loadSize() {
        return StorageUtils.getAppTotalSize(CleanApplication.getInstance(), pkgName);
    }

    /**
     * get the size in a thread, when finish, call InfoChangeListener.onChange
     *
     * @param listener could be null, mean load icon without delay
     * @return 0 if not valid or is zero
     */
    public long getSize() {
        if (!mIsValid) {
            return 0;
        }
        init();
        if (mSize >= 0) {
            return mSize;
        }

        mSize = loadSize();
        return mSize;
    }

    private long loadAppSize() {
        return StorageUtils.getAppCodeSize(CleanApplication.getInstance(), pkgName);
    }

    /**
     * get the apk size in a thread, when finish, call InfoChangeListener.onChange
     *
     * @param listener could be null, mean load icon without delay
     * @return 0 if not valid or is zero
     */
    public long getAppSize() {
        if (!mIsValid) {
            return 0;
        }
        init();
        if (mAppSize >= 0) {
            return mAppSize;
        }

        mAppSize = loadAppSize();
        return mAppSize;
    }

    /**
     * get the install time, if sdk < 9, then return the sourcedir file modify time
     *
     * @return install time in long, could be 0 if invalid
     */
    @TargetApi(9)
    public long getInstallTime() {
        if (!mIsValid) return 0;
        if (mInstallTime < 0) {
            if (android.os.Build.VERSION.SDK_INT < 9) {
                init();
                mInstallTime = mApkFile != null ? mApkFile.lastModified() : 0;
            } else {
                mInstallTime = getPackage().firstInstallTime;
            }
        }
        return mInstallTime;
    }

    /**
     * get the update time, if sdk < 9, then return the sourcedir file modify time
     *
     * @return update time in long, could be 0 if invalid
     */
    @TargetApi(9)
    public long getUpdateTime() {
        if (!mIsValid) return 0;
        if (mUpdateTime < 0) {
            if (android.os.Build.VERSION.SDK_INT < 9) {
                init();
                mUpdateTime = mApkFile != null ? mApkFile.lastModified() : 0;
            } else {
                PackageInfo pkginfo = getPackage();
                if (pkginfo != null) {
                    mUpdateTime = getPackage().lastUpdateTime;
                }
            }
        }
        return mUpdateTime;
    }

    public void onPackageUpdate() {
        mPackage = null;
        mIcon = null;
        mLabel = null;
        mSize = -1;
        mAppSize = -1;
        mApkFile = null;
        mEnabled = ENABLE_UNKNOWN;
    }

    public void onLocaleChange() {
        mLabel = null;
    }
}
